﻿// class block
// block::state -1 雷 0 未扫描 1 已扫描 2 已经标记 数字表示周围有几个 标记
// 
// class map
// map::generate(int length,int width) 放在构造函数里面 --ok
// map::show() --ok
// map::random_bomb() 判重 --ok
// map::scan(int x,int y) 

#include<vector>
#include<random>
#include<ctime>
#include<iostream>
#include<queue>

enum StateCode { FRESH = 0 , SCANNED , MARKED};

class block
{
public:
	block() :State_(StateCode::FRESH),around_bomb_num(0),bomb(false) {};

	void change_state(int code) { State_ = code; };
	int get_state() const { return State_; }; 

	void change_bomb_num(int num) { around_bomb_num = num; };
	int get_bomb_num() const { return around_bomb_num; };

	bool is_bomb() const { return bomb; };
	void change_bomb(bool state) { bomb = state; };

private:
	int State_;
	int around_bomb_num;
	bool bomb;
};

class map
{
public:
	map(int length_, int width_ ,int bomb_num_)
		:length(length_),width(width_),bomb_num(bomb_num_),flag(ResultCode::SOLVING)
	{
		std::vector<std::vector<block>> temp(width_ , 
			std::vector<block>(length_));
		mapbase = temp;
		random_bomb();
	}  //map
	void show()
	{
		//基本信息
		std::cout << "剩余标记数目 " << mark_num;
		for (const auto& rows : mapbase)
		{
			for (const auto& obj : rows)
			{
				/*if (obj.get_state() == StateCode::FRESH) 
					std::cout << '#' ;*/
				if (obj.get_state() == StateCode::SCANNED)
				{
					if (obj.get_bomb_num() == 0)
						std::cout << ' ';
					else
						std::cout << obj.get_bomb_num();
				}
				else if (obj.get_state() == StateCode::MARKED)
					std::cout << 'P';
				else
					std::cout << '#';
			}
			std::cout << '\n';
		}
	}  //void show()

	void scan(int a,int b) //扫描坐标
	{
		block& center_self = mapbase[a][b];
		if (center_self.get_state() == StateCode::MARKED)
		{
			std::cout << "此点已经被标记";
		}
		else if (center_self.is_bomb() == true)
		{
			std::cout << "踩到雷啦" << std::endl;
			flag = ResultCode::LOSE;
		}
		else
		{
			std::queue<std::pair<int, int>> q;
			q.push(std::make_pair(a, b));

			while (!q.empty())
			{
				//int x = q.front().first, y = q.front().second;
				int x, y;
				std::tie(x, y) = q.front();

				struct block_with_pos
				{
					int x;
					int y;
					block& block_;
				}; //struct nears

				//(x+1,y+1)=>(x,y+1)=>(x-1,y+1)=>(x-1,y)=>
				//	(x-1,y-1)=>(x,y-1)=>(x+1,y-1)=>(x+1,y)

				std::vector<block_with_pos> nears;

				if (x + 1 < length && y + 1 < width)
				{
					int a = x + 1, b = y + 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (y + 1 < width)
				{
					int a = x, b = y + 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (x - 1 > 0 && y + 1 < width)
				{
					int a = x - 1, b = y + 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (x - 1 > 0)
				{
					int a = x - 1, b = y;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (x - 1 > 0 && y - 1 > 0)
				{
					int a = x - 1, b = y - 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (y - 1 > 0)
				{
					int a = x, b = y - 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (x + 1 < length && y - 1 > 0)
				{
					int a = x + 1, b = y - 1;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}
				if (x + 1 < length)
				{
					int a = x + 1, b = y;
					block& near_self = mapbase[a][b];
					if (near_self.get_state() != StateCode::SCANNED)
					{
						block_with_pos t = { a,b,near_self };
						nears.push_back(t);
					}
				}

				int cnt = 0;
				for (auto& near : nears)
				{
					//if (near.block_.get_state() == StateCode::SCANNED) ;  //如果已经亮出来or下面中心为空的情况叫做SACNNED，跳过计算
					if (near.block_.is_bomb() == true) cnt++;
				}
				center_self.change_state(StateCode::SCANNED);
				center_self.change_bomb_num(cnt);
				q.pop();

				//开始搜索
				if (cnt == 0)
					// 一旦中心为空，四周无论如何都需要亮出来,除了被标记的
				{
					for (auto& near : nears)
					{
						if(near.block_.get_state()!=StateCode::MARKED)
							near.block_.change_state(StateCode::SCANNED);
					}
					for (auto& near : nears)
					{
						if (near.block_.get_state() != StateCode::MARKED)
							q.push(std::make_pair(near.x, near.y));
					}
				}
			}  //while(!q.empty)
		}  //else
	};  //void scan(int x,int y)

	void mark(int a, int b)
	{
		block& self = mapbase[a][b];
		if (self.get_state() == StateCode::SCANNED)
			//std::cout << "? 你确定要在已经扫描过的地方拉屎（bushi 吗？" << std::endl;
			std::cout << "? 此点已经展明" << std::endl;
		else if (mark_num == 0)
		{
			std::cout << "标记已经用完" << std::endl ;
		}
		else
		{
			self.change_state(StateCode::MARKED);
			mark_num--;
			show();
		}
	}

	void unmark(int a, int b)
	{
		block& self = mapbase[a][b];
		if (self.get_state() != StateCode::MARKED)
		{
			std::cout << "此点未标记";
		}
		else
		{
			self.change_state(StateCode::FRESH);
		}
	}

	enum ResultCode{LOSE=-1,SOLVING,WIN};
	int flag; //掌管输赢
	
	void check()
	{
		//std::cout << "您只有一次机会，确定提交吗" << std::endl;    放在menu里面
		for (auto& bomb_pos : bomb_poses)
		{
			int x, y;
			std::tie(x, y) = bomb_pos;
			const block& self = mapbase[x][y];
			if (self.get_state() != StateCode::MARKED)
			{
				flag = ResultCode::LOSE;
			}
		}
		flag = ResultCode::WIN;
	}

private:

	std::vector<std::vector<block>> mapbase;

	std::vector<std::pair<int, int>> bomb_poses;

	int length;
	int width;
	int bomb_num;

	int mark_num = bomb_num;

	void random_bomb()
	{
		// 随机坐标
		std::default_random_engine rng(std::time(0));
		std::uniform_int_distribution<int> distx(0, length-1);
		std::uniform_int_distribution<int> disty(0, width-1);
		for (int j = 0;j < bomb_num;j++)
		{
			int bomb_x = distx(rng), bomb_y = disty(rng);
			block& temp = mapbase[bomb_x][bomb_y];

			//这里是判重去重的过程
			if (temp.is_bomb() != true)
			{
				temp.change_bomb(true);

				bomb_poses.push_back(std::make_pair(bomb_x, bomb_y));
			}
			else
			{
				j--;
			}
		}
	}  // void random_bomb()
	
};  //class map



int main()
{
	//size:length
	//size:width WARN 输入-1问题
	//bomb_num
	//check 雷数目（彩蛋）
	map test(9, 9, 5);
	//flag
	test.show();
	std::cout << '\n';
	test.mark(3,3);
	std::cout << '\n';
	test.scan(4,4);
	test.show();
	return 0;
}